Years ago, cyber attackers only needed to find a buffer overflow or other vulnerability and use it to upload their exploitable instructions, then make the program execute those new instructions. To counter this broad vulnerability, modern operating systems enforce “write XOR execute” (WX) defenses. That is, memory is marked as either writable or executable, but not both. So exploit code that is uploaded to writable memory cannot be executed. Not surprisingly, attackers then developed a more sophisticated exploitation method.
Computer instruction sets are densely packed into a small number of bits, so accessing those bits in ways that a programmer did not originally intend can yield gadgets (i.e., groups of bits that form valid program instructions that can be strung together by an attacker to execute arbitrary attack code from an otherwise harmless program). Known as Return Oriented Programming (ROP), these types of cyber exploits have been effective and commonplace since the widespread deployment of WX defenses. Software with a single small buffer-overflow vulnerability can be hijacked into performing arbitrary computations using ROP techniques. Hackers have even developed ROP compilers that build the ROP exploits automatically by finding gadgets in the binary of a vulnerable target and stringing those gadgets together to implement the attacker's code. To counteract various software diversity defenses that try to move the gadgets around so that a previously-compiled ROP attack will fail, attackers have developed Blind ROP (BROP) attacks that perform automated reconnaissance to find the gadgets in a running program.
Various defense methods have been developed to try to foil code reuse exploits such as ROP and BROP. Some of defenses instruct binaries to change their execution semantics or automatically filter program input to prevent exploits. However, these approaches require process-level virtual machines or active monitoring by other processes, which incur a comparatively high overhead. Other approaches separate and protect exploitable data (e.g., using shadow stacks), but such approaches also incur comparatively high overhead.
To reduce overhead and to maintain compatibility with existing operating systems and software architectures, many researchers have focused on lightweight, diversity-based techniques to prevent code reuse exploits. For example, Address Space Layout Randomization (ASLR) is common in modern operating systems.
ASLR techniques function by loading program modules into different locations each time the software is started. However, ASLR does not randomize the location of the instructions within modules that have been previously loaded, so these programs are still vulnerable to ROP attacks.
Some diversity techniques modify the binaries themselves to make them less predictable by an attacker. For example: 1) compile-time diversity produces semantically equivalent binaries with different structures; 2) offline code randomization transforms a binary on disk into a functionally equivalent variant with different bytes loaded into memory; and 3) load-time code randomization makes the binary load blocks of instructions at randomized addresses. These diversity-based approaches incur comparatively lower overhead than other ROP defenses and they offer statistical guarantees against ROP attacks. Unfortunately, these compile-time, offline, and load-time diversity defenses are still susceptible to BROP attacks that perform runtime reconnaissance to map the binary and find gadgets. So even with compile-time, offline, or load-time diversity, programs that run for a significant period of time without being reloaded (e.g., all modern server architectures) are vulnerable.
Other ROP defenses modify the operating system to augment diversity. While these approaches provide promising results, they do not modify existing third-party programs to work on existing operating systems.
Another recent approach uses compile-time diversity in tandem with hardware-based enforcement mechanism that prevents adversaries from reading any code. This approach protects against memory disclosure and thereby prevents ROP and BROP attacks; however, like the above techniques, this approach requires modifying the underlying operating system or hardware.
Some runtime techniques clone executable elements in memory and toggle between them during runtime so that the attacker is unaware of which memory layout is currently executing; however, the diversity factor is not as high as the above approaches. Another recent approach combines execution-time switching with runtime diversification by instrumenting all call, return, and jump instructions. On these instrumented instructions, execution may randomly switch between executable copies while the other copy is diversified by fine-grained ASLR. While this approach prevents varieties of ROP attacks, it incurs significant runtime overhead due to a dynamic binary instrumentation framework, and it doubles the size of the binary due to executable memory cloning. Still other approaches automatically instrument the binary, but they consume a high amount of disk space, memory footprint, and/or performance overhead.